package donkeykong;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JPanel;
/**
 * 
 * @author Antonio Cebollada, Miguel Castillo, José Ignacio Castro
 *
 */
public class DonkeyKong extends JPanel implements Runnable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	
	/** Maximo de llamas a la vez en el primer nivel **/
	private int NUMERO_MAXIMO = 1;
	
	private int VALORES_ACCION = 1;
	/** Imagenes del donkey kong **/
	private Image imagenDK, imagenquieto,imagenTirarEspecial,imagenCogerBarril,imagenSoltarBarril,
					imagenTirar,imagenPecho1,imagenPecho2,imagenPrincesa1,imagenPrincesa2,
					imagenMuertoDK1,imagenMuertoDK2,imagenDesaparece;
	private int posicion_x;
	private int posicion_y;
	private int nivel;
	/** Variable que dicta que debe realizar acciones mientras no se haya completado el nivel, o haya muerto mario **/
	private boolean DK_realizando_acciones;
	private BufferedImage fondo;
	
	private Mario mario;
	private ArrayList<Barril> barriles;
	private ArrayList<Muelle> muelles;
	private ArrayList<Espectro> espectros;
	private ArrayList<Ascensor> ascensores1;
	private ArrayList<Ascensor> ascensores2;
	private boolean DK_pasar_pantalla;
	private boolean SiguienteNivel;
	private boolean raptoCompleto;
	
	private Pauline pauline;


	private boolean ascensoresLanzados;


	private boolean llamasLanzadas;

	private Clip clickClip;

	private Puntuacion_total score;
	
	private AudioInputStream musicaMuelle;

	private int nivel_dificultad;

	/**Numero maximo de enemigos que salen en la ultima pantalla **/
	private int NUMERO_MAXIMO_3 = 5;
	/** Variable que define si DK ha muerto o sigue vivo**/
	private boolean estavivo=true;
	private boolean muertodeltodo=false;


	public boolean reiniciarJuego = false;
	
	/** Contructor del Donkey kong
	 * 
	 * @param startx : posicion en x en la que sebe comenar DK
	 * @param starty : posicion en y en la que sebe comenar DK
	 * @param nv     : pantalla en la que se muestra el DK
	 * @param espectros 
	 * @param mario 
	 * @param barriles 
	 */
	public DonkeyKong(int startx, int starty, int nv, BufferedImage imagenFondo, ArrayList<Barril> b, Mario m, ArrayList<Espectro> esp, Puntuacion_total pt){
		
		try {
			
			
			espectros = esp;
			mario = m;
			nivel = nv;
			setPosicion_x(startx);
			setPosicion_y(starty);
			imagenquieto = ImageIO.read(new File("./images/donkey/donkey_1.png"));
			imagenTirarEspecial = ImageIO.read(new File("./images/donkey/donkey_2_azul.png"));
			imagenTirar = ImageIO.read(new File("./images/donkey/donkey_2.png"));
			imagenPecho1 = ImageIO.read(new File("./images/donkey/donkey_3.png"));
			imagenPecho2 = ImageIO.read(new File("./images/donkey/donkey_4.png"));
			imagenPrincesa1 = ImageIO.read(new File("./images/donkey/donkey_5.png"));
			imagenPrincesa2 = ImageIO.read(new File("./images/donkey/donkey_6.png"));
			imagenMuertoDK1 = ImageIO.read(new File("./images/donkey/donkey_7.png"));
			imagenMuertoDK2 = ImageIO.read(new File("./images/donkey/donkey_8.png"));
			imagenCogerBarril = ImageIO.read(new File("./images/donkey/donkey_9.png"));
			imagenSoltarBarril = ImageIO.read(new File("./images/donkey/donkey_10.png"));
			imagenDesaparece = ImageIO.read(new File("./images/donkey/donkey_11.png"));
			imagenDK = imagenquieto;
			DK_realizando_acciones = true;
			DK_pasar_pantalla = false;
			fondo = imagenFondo;
			barriles = b;
			score=pt;
			SiguienteNivel=false;
			setRaptoCompleto(false);
			
		} catch (IOException e) {
			e.printStackTrace();
		}	
		
	}
	

	

public DonkeyKong(int startx, int starty, int nv, BufferedImage imagenFondo, ArrayList<Muelle> mue, Mario m, ArrayList<Espectro> esp, ArrayList<Ascensor> ascen1, ArrayList<Ascensor> ascen2, Puntuacion_total pt, int nivelDificultad){
		
		try {
			
			nivel_dificultad = nivelDificultad;
			
			espectros = esp;
			mario = m;
			nivel = nv;
			setPosicion_x(startx);
			setPosicion_y(starty);
			imagenquieto = ImageIO.read(new File("./images/donkey/donkey_1.png"));
			imagenTirarEspecial = ImageIO.read(new File("./images/donkey/donkey_2_azul.png"));
			imagenTirar = ImageIO.read(new File("./images/donkey/donkey_2.png"));
			imagenPecho1 = ImageIO.read(new File("./images/donkey/donkey_3.png"));
			imagenPecho2 = ImageIO.read(new File("./images/donkey/donkey_4.png"));
			imagenPrincesa1 = ImageIO.read(new File("./images/donkey/donkey_5.png"));
			imagenPrincesa2 = ImageIO.read(new File("./images/donkey/donkey_6.png"));
			imagenMuertoDK1 = ImageIO.read(new File("./images/donkey/donkey_7.png"));
			imagenMuertoDK2 = ImageIO.read(new File("./images/donkey/donkey_8.png"));
			imagenCogerBarril = ImageIO.read(new File("./images/donkey/donkey_9.png"));
			imagenSoltarBarril = ImageIO.read(new File("./images/donkey/donkey_10.png"));
			imagenDesaparece = ImageIO.read(new File("./images/donkey/donkey_11.png"));
			imagenDK = imagenquieto;
			DK_realizando_acciones = true;
			fondo = imagenFondo;
			muelles = mue;
			ascensores1 = ascen1;
			ascensores2 = ascen2;
			score=pt;
			
		} catch (IOException e) {
			e.printStackTrace();
		}	
		
	}




	/** Funcion paintComponent que es propia de los JPanels, que sirve para dibujar en el mismo la imagen que se quiera, entre otras opciones **/
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2d = (Graphics2D) g;
		/** Para hacer operaciones complejas con una imagen, crear un AffineTransform **/
		AffineTransform at = new AffineTransform();
		at.translate(getPosicion_x(), getPosicion_y()- imagenDK.getHeight(null));
		/** Pintar a Donkey kong a buena escala, en vez de 50x50, se escala a otros valores de x e y, para hacerlo mas grande. **/
		at.scale(2.5, 2.1);   
		g2d.drawImage(imagenDK,at,null);	

	}


	
	/** Getter de la posicion x **/
	public int getPosicion_x() {
		return posicion_x;
	}
	/** Getter de la posicion y **/
	public int getPosicion_y() {
		return posicion_y;
	}
	
	/** Setter de la posicion x **/
	public void setPosicion_x(int posicion_x) {
		this.posicion_x = posicion_x;
	}
	/** Setter de la posicion y **/
	public void setPosicion_y(int posicion_y) {
		this.posicion_y = posicion_y;
	}


	/**
	 * FUNCIONES PARA LA PRIMERA PANTALLA
	 */

	private void tirarBarrilNormal() {
		try{
			imagenDK = imagenCogerBarril;
			Thread.sleep(700);
			imagenDK = imagenTirar;
			Thread.sleep(700);
			imagenDK = imagenSoltarBarril;
			Thread.sleep(500);
			if (!DK_pasar_pantalla){
				int velocidad_barril = (nivel_dificultad==1?12: (nivel_dificultad==2?15: (nivel_dificultad==3?16:18)));
				Barril b = new Barril(210, 185, fondo, velocidad_barril, mario, false, espectros,score,nivel_dificultad);   //  cambiar la velocidad segun la dificultad (12 LVL1, 15 LVL2, 16 LVL3, 17 LVL4+)
				Thread barril = new Thread(b);
				barril.setName("barril");
				barril.start();
				barriles.add(b);
			}
			golpearsePecho();
		}catch (Exception e){
			e.printStackTrace();
		}		
	}


	private void golpearsePecho() {
		try{
			imagenDK = imagenPecho1;
			Thread.sleep(500);
			imagenDK = imagenPecho2;
			Thread.sleep(500);
			imagenDK = imagenquieto;
		}catch (Exception e){
			e.printStackTrace();
		}
	}


	private void tirarBarrilEspecial() {
		try{
			imagenDK = imagenCogerBarril;
			Thread.sleep(700);
			imagenDK = imagenTirarEspecial;
			Thread.sleep(500);
			if (!DK_pasar_pantalla){
				int velocidad_barril = (nivel_dificultad==1?12: (nivel_dificultad==2?15: (nivel_dificultad==3?16:18)));
				Barril b = new Barril(150, 185, fondo, velocidad_barril, mario, true, espectros,score,nivel_dificultad);  //  cambiar la velocidad segun la dificultad (12 LVL1, 15 LVL2, 16 LVL3, 17 LVL4+)
				Thread barril = new Thread(b);
				barril.setName("barrilEspecial");
				barril.start();
				barriles.add(b);
			}
			golpearsePecho();
		}catch (Exception e){
			e.printStackTrace();
		}		
	}

	/** HASTA AQUI FUNCIONES DE LA PRIMERA PANTALLA **/

	/** FUNCIONES PARA LA SEGUNDA PANTALLA 
	 * @param desplazamientoMuelle **/
	
	private void tirarMuelle(int posX_ini) {
		try{
			
			int posY_ini = 105;
			

			Muelle mu = new Muelle(posX_ini, posY_ini, fondo, mario);  // TODO cambiar la velocidad segun la dificultad
			Thread muelle = new Thread(mu);
			
			muelle.start();
			muelles.add(mu);

		}catch (Exception e){
			e.printStackTrace();
		}		
	}
	
	public void escucharSonido(String sonido, int duracion){
		try {
			clickClip = AudioSystem.getClip();
			musicaMuelle = AudioSystem.getAudioInputStream(new File("./sounds/"+sonido+".wav"));
			clickClip.open(musicaMuelle);
			clickClip.loop(duracion);
			clickClip.start();
		} catch (LineUnavailableException e) {
			e.printStackTrace();
		} catch (UnsupportedAudioFileException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	/** Funcion que se utiliza para parar el sonido, si esta activado **/
	public void pararSonido(){
		if(clickClip != null){
			clickClip.stop();
			clickClip.close();
		}
	}
	
	
	private void sacarNuevoAscensor(int tipo) {
		try{
			
			if (tipo == 1){
				int posX_ini = 142;
				int posY_ini = 570;
				
				Ascensor as = new Ascensor(posX_ini, posY_ini, mario, 1);  // TODO cambiar la velocidad segun la dificultad
				Thread t_asc = new Thread(as);
				t_asc.start();
				ascensores1.add(as);
			}else /**tipo == 2 **/ {
				int posX_ini = 335;
				int posY_ini = 225;						
				
				Ascensor as2 = new Ascensor(posX_ini, posY_ini, mario, 2);  // TODO cambiar la velocidad segun la dificultad
				Thread t_asc2 = new Thread(as2);
				t_asc2.start();
				ascensores2.add(as2);
			}

		}catch (Exception e){
			e.printStackTrace();
		}		
	}
	
	
	

	
	
	
	/** HASTA AQUI SEGUNDA PANTALLA **/
	
	
	

	@Override
	public void run() {
		try{
			while ( true ){
				
				if (isMuerto()&nivel!=3){
					break;
				}
				
				
				if (nivel==1){
					if (! SiguienteNivel){
						int tiempo_espera = (nivel_dificultad==1?1200:(nivel_dificultad==2?1000:(nivel_dificultad==3?800:600)));
						Thread.sleep(tiempo_espera);
						Random rand = new Random();
						// 0: coger y tirar barril azul (llama), 1 : golpearse el pecho, 2,+: coger y tirar baul normal
						int accionDK = rand.nextInt(VALORES_ACCION);   //  cambiar la decision segun la dificultad (5 LVL1, 7 LVL2, 10 LVL3+)
						if (!DK_realizando_acciones){
							accionDK = 1;
						}
						if (DK_pasar_pantalla){
							
						}else{
							if (accionDK==0) {
								int num_espectros=(nivel_dificultad==1?1:(nivel_dificultad==2?2:4));
								NUMERO_MAXIMO = num_espectros;
								if (espectros.size() != NUMERO_MAXIMO){  // cambiar segun dificultad (1 LVL1, 2 LVL2, 4 LVL3+)
									tirarBarrilEspecial();
								}else{
									tirarBarrilNormal();
								}
							}else if (accionDK<2) {
								golpearsePecho();
							}else{
								tirarBarrilNormal();
							}
							int acciones = (nivel_dificultad==1?5:(nivel_dificultad==2?7:10));
							if (VALORES_ACCION!=acciones)
								VALORES_ACCION = acciones;
						}
					}else{
						/** Ponerse en la escalera, y subir con Pauline en los brazos , despues indicar que ya se puede iniciar el segundo nivel**/
						setPosicion_x(getPosicion_x()+130);
						pauline.Raptada();
						while (getPosicion_y()>60){
							setPosicion_y(getPosicion_y()-20);
							imagenDK = imagenPrincesa1;
							Thread.sleep(102);
							imagenDK = imagenPrincesa2;
							Thread.sleep(102);
						}
						// Cambiar imagen Pauline
						imagenDK = imagenDesaparece;
						Thread.sleep(1800);
						
						setRaptoCompleto(true);
						
					}
				}
				else if (nivel==2){
					if (! SiguienteNivel){
						if (DK_pasar_pantalla){
							Thread.sleep(10);
						}else{
							if (!llamasLanzadas){
								//El mono lanza las dos llamas que recorren el escenario
								/** Generar una llama **/
								int velocidad = (nivel_dificultad==1?4:(nivel_dificultad<4?5:7));
								Espectro llama = new Espectro(275, 292, true, mario, fondo,velocidad,velocidad,2,score);  //  esto cambiarlo segun dificultad
								espectros.add(llama);
								Thread espectro = new Thread(llama);
								espectro.start();
								
								/** Generar otra llama **/
								
								Espectro llama2 = new Espectro(620, 313, true, mario, fondo,velocidad,velocidad,2,score);  //  esto cambiarlo segun dificultad
								espectros.add(llama2);
								Thread espectro2 = new Thread(llama2);
								espectro2.start();
		
								llamasLanzadas = true;
							}
							
							if (!ascensoresLanzados){
								
								/** 47pixeles largo ascensor**/
								/*250, 373, 483*/
								//Sacamos los ascensores
								Ascensor ascensor11 = new Ascensor(142, 503, mario, 1);
								ascensores1.add(ascensor11);
								Thread asc11 = new Thread(ascensor11);
								asc11.start();
								
								Ascensor ascensor12 = new Ascensor(142, 403, mario, 1);
								ascensores1.add(ascensor12);
								Thread asc12 = new Thread(ascensor12);
								asc12.start();
								
								Ascensor ascensor13 = new Ascensor(142, 313, mario, 1);
								ascensores1.add(ascensor13);
								Thread asc13 = new Thread(ascensor13);
								asc13.start();
								
								/*503, 403, 313 */
								Ascensor ascensor21 = new Ascensor(335, 250, mario, 2);
								ascensores2.add(ascensor21);
								Thread asc21 = new Thread(ascensor21);
								asc21.start();
								
								Ascensor ascensor22 = new Ascensor(335, 373, mario, 2);
								ascensores2.add(ascensor22);
								Thread asc22 = new Thread(ascensor22);
								asc22.start();
								
								Ascensor ascensor23 = new Ascensor(335, 483, mario, 2);
								ascensores2.add(ascensor23);
								Thread asc23 = new Thread(ascensor23);
								asc23.start();
								
								ascensoresLanzados = true;
								
								
								
							}
							//El mono ha sacado las llamas. Ahora se encarga de lanzar los muelles. Esperamos 1.5 segundos entre muelle y muelle
							int tiempo_espera = (nivel_dificultad==1?1500:(nivel_dificultad==2?1200:(nivel_dificultad==3?1000:700)));

							Thread.sleep(tiempo_espera);
							if (DK_pasar_pantalla){
								pararSonido();
							}else{
								golpearsePecho();
								if (!mario.isMuriendo()){
								//Aqui habra que lanzar los muelles
								Random ram = new Random();
								int desplazamientoMuelle = ram.nextInt(80);
								if (!DK_pasar_pantalla)
									tirarMuelle(desplazamientoMuelle);
								//Lanzar musica del muelle
								
									Thread.sleep(420);
									escucharSonido("Muelles",0);
			
									sacarNuevoAscensor(1);
									sacarNuevoAscensor(2);
								}else{
									pararSonido();
								}
							}
						}
					}else{
						/** Ponerse en la escalera, y subir con Pauline en los brazos , despues indicar que ya se puede iniciar el segundo nivel**/
						pararSonido();
						setPosicion_x(getPosicion_x()+90);
						pauline.Raptada();
						while (getPosicion_y()>100){
							setPosicion_y(getPosicion_y()-5);
							imagenDK = imagenPrincesa1;
							Thread.sleep(102);
							imagenDK = imagenPrincesa2;
							Thread.sleep(102);
						}
						// Cambiar imagen Pauline
						imagenDK = imagenDesaparece;
						Thread.sleep(1800);
						
						setRaptoCompleto(true);
						
					}
						
						
				}else{
					if (muertodeltodo){
						
					}else{
					if(!isMuerto()){
						
						Thread.sleep(3000);	
						if (isMuerto()){
							escucharSonido("DK_Cae", 0);
							Thread.sleep(3500);
							imagenDK=imagenMuertoDK1;
							while (getPosicion_y()<420){
								setPosicion_y(getPosicion_y()+20);			
								Thread.sleep(90);	
								
							}
							imagenDK=imagenMuertoDK2;
							Thread.sleep(2500);	
							escucharSonido("Ending_Theme", 0);
							muertodeltodo=true;
							Thread.sleep(8000);
							reiniciarJuego = true;
							
							
						}
						golpearsePecho();
						if (espectros.size() != NUMERO_MAXIMO_3 ){ 					
							
							Random nombre = new Random();
							int rand=nombre.nextInt(4);
							int plataf;
							//nombre.nextInt((max1-min1)+1)+min);
							if(rand==0){
								plataf=480;
							}
							else if(rand==1){
								plataf=376;
							}
							else if(rand==2){
								plataf=272;
							}
							else {
								plataf=167;
							}
							
							if(mario.getPosicion_x()<395){
								
								rand = nombre.nextInt(((630-590)+1))+590;
								
							}
							else{
								
								rand = nombre.nextInt(((182-135)+1))+135;
							}
							int velocidad = (nivel_dificultad==1?4:(nivel_dificultad<4?5:7));
							Espectro llama = new Espectro(rand, plataf, false, mario, fondo,velocidad,velocidad,3,score);  //TODO  esto cambiarlo segun dificultad
							espectros.add(llama);
							Thread espectro = new Thread(llama);
							espectro.start();
						
						}
					}else{
						escucharSonido("DK_Cae", 0);
						Thread.sleep(3500);
						imagenDK=imagenMuertoDK1;
						while (getPosicion_y()<420){
							setPosicion_y(getPosicion_y()+20);			
							Thread.sleep(80);	
							
						}
						imagenDK=imagenMuertoDK2;
						Thread.sleep(1500);	
						escucharSonido("Ending_Theme", 0);
						muertodeltodo=true;
						Thread.sleep(8000);
						reiniciarJuego=true;
					}
					}
					repaint();
					}
			}
		}catch (Exception e){
			e.printStackTrace();
		}
	}




	/** Funcion que se dispara cuando Mario muere **/
	public void DKparado(){
		 DK_realizando_acciones = false;
	}

	/** Funcion que se dispara al pasarse la pantalla **/
	public void DKPasarPantalla(){
		DK_pasar_pantalla = true;
		imagenDK = imagenquieto;
	}



	/** Funcion que indica al runnable que se debe pasar de nivel, mostrando las animaciones necesarias **/
	public void PasarSiguienteNivel() {
		SiguienteNivel=true;
	}



	/** Devuelve el valor del booleano que indica si DK se ha llevado a Pauline a otro nivel **/
	public boolean raptoCompleto() {
		return raptoCompleto;
	}
	
	/** Modifica el valor del booleano que indica si DK se ha llevado a Pauline a otro nivel **/
	private void setRaptoCompleto(boolean b) {
		raptoCompleto = b;
	}




	public void Pauline(Pauline p) {
		pauline = p;
	}



	//public boolean isMuerto(){
	//	return morir;
	//}
	
	//public void morir() {
	//	morir=true;
	//}

	public boolean isMuerto(){
		
		return !estavivo;
	}
	
	public void morir() {
		estavivo=false;
	}

	public boolean isMuertodeltodo() {
		// TODO Auto-generated method stub
		return muertodeltodo;
	}
}
